#include "search.h"

/*
  TODO: does not seem to make moves, which CHECK the king...
  */

// INIT

void Search::init() {
    evaluater_.calcBoardScore(); // set initial score value;
    nodeCounter = alphaBetaCutOffs = 0;
    currentBestMoveLine.clear();
    finalBestMoveLine.clear();
    setStartSideToMove(BLACK_TO_MOVE);
    board_.gameLine.init(40, board_.enPassantSquare);
    qDebug() << "Search initialised";
}

void Search::setSearchDepth(size_t depth) {
    searchDepth_ = depth;
    currentBestMoveLine.resize(depth+1);
    finalBestMoveLine.resize(depth+1);
    qDebug() << "Set Search Depth to " + depth;
}

size_t Search::getSearchDepth() {
    return searchDepth_;
}

void Search::setStartSideToMove(bool startSideToMove) {
    startSideToMove_ = startSideToMove;
}

MoveMaker Search::getMoveMaker() {
    return moveMaker_;
}

// THINK


// thinking method of primchess
// performing
// - check for end of game
// - search (various methodical approaches) (negamax...)
// - user interaction
void Search::doThink() {

}

void Search::DebugInit() {
    // debug status and flags of the board before and after do/undo a move
    dWK = board_.wK;dWQ = board_.wQ; dWR = board_.wR;
    dWB = board_.wB;dWN = board_.wN;dWP = board_.wP;
    dBK = board_.bK; dBQ = board_.bQ;dBR = board_.bR;
    dBB = board_.bB; dBN = board_.bN;dBP = board_.bP;
    ePS = board_.enPassantSquare;
    wCC00 = board_.wCanCastle00;
    wCC000 = board_.wCanCastle000;
    bCC00 = board_.bCanCastle00;
    bCC000 = board_.bCanCastle000;
}

void Search::DebugCheck(Move move) {
    // debug flags
    if ((board_.enPassantSquare!= ePS)
            || (board_.wCanCastle00 != wCC00)
            || (board_.wCanCastle000 != wCC000)
            || (board_.bCanCastle00 != bCC00)
            || (board_.bCanCastle000 != bCC000)) {
        qDebug() << move.from;
        qDebug() << move.to;
        qDebug() << "ERROR IN UNMAKE MOVE -- FLAGS";
        exit(1);
    }

    // debug white pieces
    if ((board_.wK != dWK) || (board_.wQ != dWQ) || (board_.wR != dWR)
         || (board_.wB != dWB) || (board_.wN != dWN) || (board_.wP != dWP)) {
        qDebug() << move.from;
        qDebug() << move.to;
        qDebug() << "ERROR IN UNMAKE WHITE MOVE";
        exit(1);
    }
    // debug black pieces
    if ((board_.bK != dBK) || (board_.bQ != dBQ) || (board_.bR != dBR)
         || (board_.bB != dBB) || (board_.bN != dBN) || (board_.bP != dBP)) {
        qDebug() << move.from;
        qDebug() << move.to;
        qDebug() << "ERROR IN UNMAKE BLACK MOVE";
        exit(1);
    }

}

// SEARCH

// wikipedia : negamax
/*
 int NegaMax(int tiefe,
             int alpha,
             int beta)
 {
    if (tiefe == 0)
       return Bewerten();
    GeneriereMoeglicheZuege();
    localAlpha = -unendlich;
    while (ZuegeUebrig())
    {
       FuehreNaechstenZugAus();
       wert = -NegaMax(tiefe-1,
                       -beta,
                       -alpha);
       MacheZugRueckgaengig();
       if (wert > localAlpha)
       {
          if (wert > alpha)
             alpha = wert;
          localAlpha = wert;
          if (alpha >= beta)
             break;
       }
    }
    return localAlpha;
 }*/

int Search::chooseRandomMove() {
    board_.sideToMove = startSideToMove_;
    moveGenerator_.generateMoves();
    std::vector<Move> moveList = moveGenerator_.getMoveList();

    int randomIndex = rand() % moveList.size();

    currentBestMoveLine.at(getSearchDepth()) = moveList.at(randomIndex);

    return 0;
}

// negaMax search algorithm
// missing 50 moves, 3 repetition
int Search::negaMax(int depth, int alpha, int beta)
{
    if (depth == 0) return evaluater_.getScore();

    nodeCounter++;
    int movesFound = 0;
    int score;
    int localAlpha = -INFINITY;

    if ((searchDepth_ - depth) % 2 == 0)
        board_.sideToMove = startSideToMove_;
    else
        board_.sideToMove = !startSideToMove_;

    moveGenerator_.generateMoves();
    std::vector<Move> moveList = moveGenerator_.getMoveList();
    for (size_t i = 0; i < moveList.size(); i++) {
        Move move(moveList.at(i));
        DebugInit();

        // START MOVE
        moveMaker_.doMove(move);

        if (!moveGenerator_.isOtherKingAttacked())
        {
            movesFound++;

            score = - negaMax (depth - 1, -beta, -alpha);
            moveMaker_.undoMove(moveList.at(i));

            if (score > localAlpha) {
                if (score > alpha) {
                    alpha = score;
                }
                localAlpha = score;

                if (alpha >= beta)
                    { alphaBetaCutOffs++; break; }

                // save best move
                currentBestMoveLine.at(depth) = moveList.at(i);
            }
        }
        else
            moveMaker_.undoMove(moveList.at(i));
        // END MOVE

        DebugCheck(move);
    } // end of moveList

    finalBestMoveLine = currentBestMoveLine;

    // no moves found
    if (!movesFound)
        if (moveGenerator_.isOwnKingAttacked())
        {
            QNoDebug() << "CHECKMATE FOUND";
                 return (-CHECK_MATE-depth);
        }

        else
            return (STALE_MATE);

    return localAlpha;
}



U64 Search::perft(int ply, int depth)
{

 U64 retVal = 0;
 int i;

 // count this node
 if (depth == 0)
 {
        #ifdef WINGLET_DEBUG_EVAL
               int before = board.eval();
               board.mirror();
               int after = board.eval();
               board.mirror();
               if (before != after)
               {
                     std::cout << "evaluation is not symmetrical! " << before << std::endl;
                     for (int j = 0 ; j < board.endOfSearch ; j++)
               {
                            std::cout << j+1 << ". ";
                            displayMove(board.gameLine[j].move);
                            std::cout <<std::endl;
                     }
                     board.display();
               }
        #endif
        return 1;
 }

 moveGenerator_.generateMoves();
 std::vector<Move> moveList = moveGenerator_.getMoveList();
 // loop over moves:
for (size_t i = 0; i < moveList.size(); i++) {
        Move move(moveList.at(i));
        moveMaker_.doMove(move);
        {
               if (!moveGenerator_.isOtherKingAttacked())
               {
                     // recursively call perft
                     retVal += perft(ply + 1, depth-1);

                     #ifdef WINGLET_DEBUG_PERFT
                     if (depth == 1)
                     {
                            if (board.moveBuffer[i].isCapture()) ICAPT++;
                            if (board.moveBuffer[i].isEnpassant()) IEP++;
                            if (board.moveBuffer[i].isPromotion()) IPROM++;
                            if (board.moveBuffer[i].isCastleOO()) ICASTLOO++;
                            if (board.moveBuffer[i].isCastleOOO()) ICASTLOOO++;
                            if (isOwnKingAttacked()) ICHECK++;
                     }
                    #endif
               }
        }
        moveMaker_.undoMove(move);
 }
 return retVal;
}





//makeMove(moveBuffer[i]);
//              {
//                     if (!isOtherKingAttacked())
//                     {
//                           inodes++;
//                           movesfound++;
//                           if (!ply) displaySearchStats(3, ply, i);
//                           if (pvmovesfound)
//                           {
//                                  val = -alphabetapvs(ply+1, depth-1, -alpha-1, -alpha);
//                          if ((val > alpha) && (val < beta))
//                                  {
//                                         val = -alphabetapvs(ply+1, depth - 1, -beta, -alpha);   // In case of failure, proceed with normal alphabeta
//                                  }
//                           }
//                            else val = -alphabetapvs(ply+1, depth-1, -beta, -alpha);          // Normal alphabeta
//                           unmakeMove(moveBuffer[i]);
//                           if (val >= beta)
//                           {
//                                  return beta;
//                           }
//                           if (val > alpha)
//                           {
//                                  alpha = val;                                                      // both sides want to maximize from *their* perspective
//                                  pvmovesfound++;
//                                  triangularArray[ply][ply] = moveBuffer[i];                                  // save this move
//                                  for (j = ply + 1; j < triangularLength[ply+1]; j++)
//                                  {
//                                         triangularArray[ply][j] = triangularArray[ply+1][j];   // and append the latest best PV from deeper plies
//                                  }
//                                  triangularLength[ply] = triangularLength[ply+1];
//                                  if (!ply)
//                                  {
//                                         msStop = timer.getms();
//                                         displaySearchStats(2, depth, val);
//                                  }
//                           }
//                     }
//                     else unmakeMove(moveBuffer[i]);
//              }



